﻿using System;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Threading;
using System.ComponentModel;

namespace System.Windows.Controls
{
    /// <summary>
    /// Implements a view control which displays items based on a configurable or user-selectable horizontal axis and a vertical
    /// axis.
    /// </summary>
    [TemplatePart(Name="AxisSelector", Type=typeof(ComboBox))]
    [TemplatePart(Name="RelevancyLabel", Type=typeof(TextBlock))]
    public class CloudView : System.Windows.Controls.SelectionItemsControl
    {
        /// <summary>
        /// An event which fires when the <see cref="Magnification"/> property has changed.
        /// </summary>
        public event EventHandler MagnificationChanged = null;

        /// <summary>
        /// An event which fires when the <see cref="CurrentArranger"/> has changed.
        /// </summary>
        public event EventHandler ArrangerChanged = null;

        /// <summary>
        /// Instantiates a <see cref="CloudView"/> instance.
        /// </summary>
        public CloudView()
        {
            this.DefaultStyleKey = typeof(CloudView);
            this.Arrangers = CloudArrangers.Default;
            this.UseItemAnimation = true;

            animTimer = new Timer(new TimerCallback(this.OnAnimateItemLoop));
            animTimer.Change(Timeout.Infinite, Timeout.Infinite);
        }

        /// <summary>
        /// Identifies the <see cref="Arrangers"/> depedency property.
        /// </summary>
        public static readonly DependencyProperty ArrangersProperty = RegisterDependencyProperty(typeof(CloudView), "Arrangers", typeof(CloudArrangers));

        /// <summary>
        /// Identifies the <see cref="UseItemAnimation"/> depedency property.
        /// </summary>
        public static readonly DependencyProperty UseItemAnimationProperty = RegisterDependencyProperty(typeof(CloudView), "UseItemAnimation", typeof(bool));

        /// <summary>
        /// (Override) Handles dependency property changes for this <see cref="CloudView"/> instance.
        /// </summary>
        /// <param name="e">The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> event argument</param>
        protected override void OnDependencyPropertyChanged(DependencyPropertyChangedEventArgs e)
        {
            if (e.Property == ArrangersProperty)
            {
                this.Arrangers = e.NewValue as CloudArrangers;
            }
            else if (e.Property == UseItemAnimationProperty)
            {
                this.UseItemAnimation = (bool)e.NewValue;
            }
            else
            {
                base.OnDependencyPropertyChanged(e);
            }
        }

        private ComboBox        axisSelector;
        private CloudArrangers  arrangers = null;
        private ICloudArranger  _CurrentArranger = null;
        private TextBlock       axisLeftLabel;
        private TextBlock       axisRightLabel;
        private TextBlock       relevancyLabel;
        private ScrollViewer    viewer;
        private CloudPanel      cloudPanel = null;
        private Slider          magSlider;
        private TextBlock       magLabel;
        private CloudViewItems  _Items = new CloudViewItems();

        private Timer           animTimer;
        private int             animIndex = 0;

        /// <summary>
        /// Gets/Sets a boolean value which determines whether the <see cref="CloudViewItem"/> instances are animated into position following a data binding operation.
        /// </summary>
        public bool UseItemAnimation
        {
            get { return (bool)GetValue(UseItemAnimationProperty); }
            set { SetValue(UseItemAnimationProperty, value); }
        }

        /// <summary>
        /// Gets/Sets a double value which specifies the current magnification for the <see cref="CloudView"/> instance.
        /// </summary>
        public double Magnification
        {
            get { return (magSlider != null ? magSlider.Value : 1.0); }
            set { if (magSlider != null) magSlider.Value = value; }
        }

        /// <summary>
        /// Gets a <see cref="Size"/> structure which describes the width and height of the viewable area of the control.
        /// </summary>
        public Size ViewportSize
        {
            get
            {
                if (viewer != null)
                {
                    return new Size(Math.Max(0, viewer.ActualWidth - 48), Math.Max(0, viewer.ActualHeight - 48));
                }

                return Size.Empty;
            }
        }

        /// <summary>
        /// Gets an instance of <see cref="CloudArrangers"/> which collect instances of <see cref="ICloudArranger"/>.
        /// </summary>
        public CloudArrangers Arrangers
        {
            get { return arrangers; }
            set 
            {
                if (value != null)
                {
                    if (arrangers != null) arrangers.CollectionChanged -= this.OnArrangersChanged;
                    arrangers = value;
                    arrangers.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(OnArrangersChanged);
                    BindArrangers();
                }
            }
        }

        /// <summary>
        /// Gets the active <see cref="ICloudArranger"/> instance which is corrently handling the arrangement of the items.
        /// </summary>
        public ICloudArranger CurrentArranger
        {
            get { return _CurrentArranger; }
            set 
            { 
                _CurrentArranger = value;

                if (_CurrentArranger != null)
                {
                    if (axisLeftLabel != null) axisLeftLabel.Text = _CurrentArranger.MinAxisLabel;
                    if (axisRightLabel != null) axisRightLabel.Text = _CurrentArranger.MaxAxisLabel;

                    if (_Items != null)
                    {
                        for (int index = 0; index < _Items.Count; index++)
                        {
                            _Items[index].Opacity = 0;
                        }
                    }

                    if (null != this.ArrangerChanged) this.ArrangerChanged(this, EventArgs.Empty);
                }
            }
        }

        void OnArrangersChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (axisSelector != null)
            {
                axisSelector.Items.Clear();

                for(int index = 0; index < e.NewItems.Count; index++)
                {
                    ICloudArranger arranger = e.NewItems[index] as ICloudArranger;
                    if (arranger != null)
                    {
                        ComboBoxItem item = new ComboBoxItem();
                        item.Content = arranger.Title;
                        item.Tag = arranger;
                        axisSelector.Items.Add(item);
                    }
                }

                if (axisSelector.Items.Count > 0) axisSelector.SelectedItem = axisSelector.Items[0] as ComboBoxItem;
            }
        }

        private void BindArrangers()
        {
            if (axisSelector != null)
            {
                axisSelector.Items.Clear();

                for (int index = 0; index < arrangers.Count; index++)
                {
                    ICloudArranger arranger = arrangers[index];
                    ComboBoxItem item = new ComboBoxItem();
                    item.Content = arranger.Title;
                    item.Tag = arranger;
                    axisSelector.Items.Add(item);
                }

                if (axisSelector.Items.Count > 0)
                {
                    axisSelector.SelectedItem = axisSelector.Items[0] as ComboBoxItem;
                    if (axisSelector.SelectedItem != null)
                    {
                        ComboBoxItem item = axisSelector.SelectedItem as ComboBoxItem;
                        if (item != null)
                        {
                            ICloudArranger arranger = item.Tag as ICloudArranger;
                            if (arranger != null) this.CurrentArranger = arranger;
                        }
                    }
                }

                axisSelector.InvalidateArrange();
            }
        }

        internal void CenterScrollOrigin()
        {
            if (viewer != null)
            {
                viewer.ScrollToHorizontalOffset(viewer.ScrollableWidth / 2);
                viewer.ScrollToVerticalOffset(viewer.ScrollableHeight / 2);
            }
        }

        /// <summary>
        /// (Override) Handler for when items in the control have changed.
        /// </summary>
        /// <param name="e"></param>
        protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
        {
            base.OnItemsChanged(e);
            if (cloudPanel != null) cloudPanel.InvalidateMeasure();
        }

        /// <summary>
        /// Examines an item to determine if it is the appropriate item container for this control
        /// </summary>
        /// <param name="item">The item to examine</param>
        /// <returns>A boolean value indicating that the item is the correct container (true) or not (false).</returns>
        protected override bool IsItemItsOwnContainerOverride(object item)
        {
            return (item is CloudViewItem);
        }

        /// <summary>
        /// Creates a container item for this control
        /// </summary>
        /// <returns>A <see cref="System.Windows.DependencyObject"/> instance</returns>
        protected override DependencyObject GetContainerForItemOverride()
        {
            return new CloudViewItem();
        }

        /// <summary>
        /// Prepares an item element for insertion into this control
        /// </summary>
        /// <param name="element">The visual element to being inserted</param>
        /// <param name="item">The data item associated with the visual element</param>
        protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
        {
            base.PrepareContainerForItemOverride(element, item);

            if (element is CloudViewItem)
            {
                CloudViewItem viewItem = (CloudViewItem)element;
                if (!_Items.Contains(viewItem)) _Items.Add(viewItem);

                viewItem.Opacity = (this.UseItemAnimation ? 0 : 1);
            }
        }

        /// <summary>
        /// Performs removal record-keeping for an item container used by this control
        /// </summary>
        /// <param name="element">The visual element being removed</param>
        /// <param name="item">The data item associated with the visual element</param>
        protected override void ClearContainerForItemOverride(DependencyObject element, object item)
        {
            if (element is CloudViewItem)
            {
                CloudViewItem viewItem = (CloudViewItem)element;
                if (_Items.Contains(viewItem)) _Items.Remove(viewItem);
            }

            base.ClearContainerForItemOverride(element, item);
        }

        /// <summary>
        /// (Override) Processes the control template for this <see cref="CloudView"/> instance.
        /// </summary>
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            axisSelector = VerifyTemplateChild<ComboBox>("AxisSelector");
            axisLeftLabel = VerifyTemplateChild<TextBlock>("AxisLeftLabel");
            axisRightLabel = VerifyTemplateChild<TextBlock>("AxisRightLabel");
            relevancyLabel = GetTemplateChild("RelevancyLabel") as TextBlock;
            magSlider = VerifyTemplateChild<Slider>("MagSlider");
            viewer = VerifyTemplateChild<ScrollViewer>("ScrollViewer");
            magLabel = VerifyTemplateChild<TextBlock>("MagLabel");

            //Set Vertical Axis Labels
            RepeatButton topAxisLabel = GetTemplateChild("TopAxisLabel") as RepeatButton;
            if (topAxisLabel != null) ToolTipService.SetToolTip(topAxisLabel, SystemResources.MORE_RELEVANT);
            RepeatButton bottomAxisLabel = GetTemplateChild("BottomAxisLabel") as RepeatButton;
            if (bottomAxisLabel != null) ToolTipService.SetToolTip(bottomAxisLabel, SystemResources.LESS_RELEVANT);

            if (axisSelector != null)
            {
                axisSelector.SelectionChanged += new SelectionChangedEventHandler(OnArrangerSelected);
                BindArrangers();
            }

            if (magSlider != null)
            {
                magSlider.ValueChanged += new RoutedPropertyChangedEventHandler<double>(OnMagnificationChanged);
            }

            if (relevancyLabel != null) relevancyLabel.Text = SystemResources.RELEVANCY;
        }

        void OnArrangerSelected(object sender, SelectionChangedEventArgs e)
        {
            if (axisSelector != null && axisSelector.SelectedItem != null)
            {
                ComboBoxItem item = axisSelector.SelectedItem as ComboBoxItem;
                if (item != null)
                {
                    this.CurrentArranger = item.Tag as ICloudArranger;
                }
            }
        }

        void OnMagnificationChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            if (null != magLabel) magLabel.Text = String.Format("{0}%", (int)(this.Magnification * 100.0));
            if (null != cloudPanel)
            {
                cloudPanel.Magnification = this.Magnification;
            }
            if (null != this.MagnificationChanged) this.MagnificationChanged(this, EventArgs.Empty);
        }

        private void VerifyPanel()
        {
            if (cloudPanel == null)
            {
                if (viewer != null && viewer.Content != null)
                {
                    ItemsPresenter presenter = (ItemsPresenter)viewer.Content;
                    int count = VisualTreeHelper.GetChildrenCount(presenter);
                    for (int index = 0; index < count; index++)
                    {
                        DependencyObject panelObj = VisualTreeHelper.GetChild(presenter, index);
                        if (panelObj is CloudPanel)
                        {
                            //Get a reference to the Cloud Panel for interaction relative to
                            //magnification and arranger selection
                            cloudPanel = (CloudPanel)panelObj;
                            cloudPanel.ParentView = this;
                            break;
                        }
                    }
                }
            }
        }

        internal void ArrangeCloudItems(Size tileSize)
        {
            if (_Items.Count > 0 && this.CurrentArranger != null)
            {
                Size portSize = this.ViewportSize;
                Size size = new Size(Math.Max(0, portSize.Width - tileSize.Width), Math.Max(0, portSize.Height - tileSize.Height));
                this.CurrentArranger.ArrangeChildren(_Items, size);
            }
        }

        internal void AnimateCloudItems()
        {
            for (int index = 0; index < _Items.Count; index++)
            {
                _Items[index].Opacity = 0;
            }

            animIndex = 0;
            animTimer.Change(0, 50);
        }

        private void OnAnimateItemLoop(object context)
        {
            if (!DesignerProperties.IsInDesignTool)
            {
                Dispatcher.BeginInvoke(new Action(AdvanceAnimation));
            }
        }

        private void AdvanceAnimation()
        {
            if (animIndex >= 0 && animIndex < _Items.Count)
            {
                _Items[animIndex].StartAnimation();
                animIndex++;
            }
            else
            {
                animTimer.Change(Timeout.Infinite, Timeout.Infinite);
            }
        }

        /// <summary>
        /// (Override) Performs custom steps during an Arrange pass.
        /// </summary>
        /// <param name="finalSize">The size of the control</param>
        /// <returns>The arranged size of the control</returns>
        protected override Size ArrangeOverride(Size finalSize)
        {
            VerifyPanel();
            Size size = base.ArrangeOverride(finalSize);
            if (cloudPanel != null) cloudPanel.InvalidateArrange();
            return size;
        }
    }
}
